package redis

import (
	"context"
	"github.com/redis/go-redis/v9"
	"niu-video/config"
	"niu-video/pkg/logger"
	"sync"
	"time"
)

type RedisClient struct {
	Client  *redis.Client
	Context context.Context
}

// once 确保全局的 Redis 对象只实例一次
var once sync.Once

var Redis *RedisClient

func InitRedis() {
	once.Do(func() {
		Redis = &RedisClient{
			Client: redis.NewClient(&redis.Options{
				Addr:     config.Conf.Redis.Addr,     //地址，这里使用本地服务器的默认端口
				Password: config.Conf.Redis.Password, //访问Redis的密码
				DB:       config.Conf.Redis.DB,       //使用默认的0号DB
			}),
			Context: context.Background(),
		}

		err := Redis.Ping()
		logger.LogIf(err)
	})
}

// Ping 用以测试 redis 连接是否正常
func (rds RedisClient) Ping() error {
	_, err := rds.Client.Ping(rds.Context).Result()
	return err
}

// Set 存储 key 对应的 value，且设置 expiration 过期时间
func (rds RedisClient) Set(key string, value interface{}, expiration time.Duration) bool {
	if err := rds.Client.Set(rds.Context, key, value, expiration).Err(); err != nil {
		logger.ErrorString("Redis", "Set", err.Error())
		return false
	}
	return true
}

// Get 获取 key 对应的 value
func (rds RedisClient) Get(key string) string {
	result, err := rds.Client.Get(rds.Context, key).Result()
	if err != nil {
		if err != redis.Nil {
			logger.ErrorString("Redis", "GetOne", err.Error())
		}
		return ""
	}
	return result
}

// Has 判断一个 key 是否存在，内部错误和 redis.Nil 都返回 false
func (rds RedisClient) Has(key string) bool {
	_, err := rds.Client.Get(rds.Context, key).Result()
	if err != nil {
		if err != redis.Nil {
			logger.ErrorString("Redis", "Has", err.Error())
		}
		return false
	}
	return true
}

// Del 删除存储在 redis 里的数据，支持多个 key 传参
func (rds RedisClient) Del(keys ...string) bool {
	if err := rds.Client.Del(rds.Context, keys...).Err(); err != nil {
		logger.ErrorString("Redis", "Del", err.Error())
		return false
	}
	return true
}

// FlushDB 清空当前 redis db 里的所有数据
func (rds RedisClient) FlushDB() bool {
	if err := rds.Client.FlushDB(rds.Context).Err(); err != nil {
		logger.ErrorString("Redis", "FlushDB", err.Error())
		return false
	}
	return true
}

// Increment 当参数只有 1 个时，为 key，其值增加 1。
// 当参数有 2 个时，第一个参数为 key ，第二个参数为要增加的值 int64 类型。
func (rds RedisClient) Increment(parameters ...interface{}) bool {
	switch len(parameters) {
	case 1:
		key := parameters[0].(string)
		if err := rds.Client.Incr(rds.Context, key).Err(); err != nil {
			logger.ErrorString("Redis", "Increment", err.Error())
			return false
		}
	case 2:
		key := parameters[0].(string)
		value := parameters[1].(int64)
		if err := rds.Client.IncrBy(rds.Context, key, value).Err(); err != nil {
			logger.ErrorString("Redis", "Increment", err.Error())
			return false
		}
	default:
		logger.ErrorString("Redis", "Increment", "参数过多")
		return false
	}
	return true
}

// Decrement 当参数只有 1 个时，为 key，其值减去 1。
// 当参数有 2 个时，第一个参数为 key ，第二个参数为要减去的值 int64 类型。
func (rds RedisClient) Decrement(parameters ...interface{}) bool {
	switch len(parameters) {
	case 1:
		key := parameters[0].(string)
		if err := rds.Client.Decr(rds.Context, key).Err(); err != nil {
			logger.ErrorString("Redis", "Decrement", err.Error())
			return false
		}
	case 2:
		key := parameters[0].(string)
		value := parameters[1].(int64)
		if err := rds.Client.DecrBy(rds.Context, key, value).Err(); err != nil {
			logger.ErrorString("Redis", "Decrement", err.Error())
			return false
		}
	default:
		logger.ErrorString("Redis", "Decrement", "参数过多")
		return false
	}
	return true
}

// 集合添加元素
func (rds RedisClient) SAdd(key string, members ...interface{}) bool {
	if err := rds.Client.SAdd(rds.Context, key, members).Err(); err != nil {
		logger.ErrorString("Redis", "SAdd", err.Error())
		return false
	}
	return true
}

// 集合删除元素
func (rds RedisClient) SRem(key string, members ...interface{}) bool {
	if err := rds.Client.SRem(rds.Context, key, members).Err(); err != nil {
		logger.ErrorString("Redis", "SRem", err.Error())
		return false
	}
	return true
}

// 集合长度
func (rds RedisClient) SCard(key string) int64 {
	cnt, err := rds.Client.SCard(rds.Context, key).Result()
	if err != nil {
		logger.ErrorString("Redis", "SCard", err.Error())
		return 0
	}
	return cnt
}

// 返回所有成员
func (rds RedisClient) SMembers(key string) []string {
	userIds, err := rds.Client.SMembers(rds.Context, key).Result()
	if err != nil {
		logger.ErrorString("Redis", "SMembers", err.Error())
		return []string{}
	}
	return userIds
}

// 这个集合中存不存在
func (rds RedisClient) SIsMember(key string, member interface{}) bool {
	is, err := rds.Client.SIsMember(rds.Context, key, member).Result()
	if err != nil {
		logger.ErrorString("Redis", "SIsMember", err.Error())
		return false
	}
	return is
}

// setnx
func (rds RedisClient) SetNX(key string, value interface{}, expiration time.Duration) bool {
	is, err := rds.Client.SetNX(rds.Context, key, value, expiration).Result()
	if err != nil {
		logger.ErrorString("Redis", "SetNX", err.Error())
		return false
	}
	return is
}

// setnx
func (rds RedisClient) MGet(keys ...string) interface{} {
	is, err := rds.Client.MGet(rds.Context, keys...).Result()
	if err != nil {
		logger.ErrorString("Redis", "MGet", err.Error())
		return false
	}
	return is
}

// lua test, modify counter in redis
const luaScript = `
	 if redis.call('SETNX',KEYS[1],1) == 1
	then
		redis.call('EXPIRE',KEYS[1],864000)
		redis.call('INCR',KEYS[2])
	end
	return redis.call('GET',KEYS[2])
`

func (rds RedisClient) Eval(script string, keys []string, args ...interface{}) bool {
	_, err := rds.Client.Eval(rds.Context, script, keys, args).Result()
	if err != redis.Nil {
		logger.ErrorString("Redis", "Eval", err.Error())
		return false
	}
	return true
}

func (rds RedisClient) Scan(cursor uint64, match string) []string {
	res, _, err := rds.Client.Scan(rds.Context, cursor, match, -1).Result()
	if err != redis.Nil {
		logger.ErrorString("Redis", "Scan", err.Error())
		return []string{}
	}
	return res
}

func (rds RedisClient) LRange(key string, start int64, stop int64) []string {
	res, err := rds.Client.LRange(rds.Context, key, start, stop).Result()
	if err != nil && err != redis.Nil {
		logger.ErrorString("Redis", "LRange", err.Error())
		return []string{}
	}
	return res
}

func (rds RedisClient) LLen(key string) int64 {
	res, err := rds.Client.LLen(rds.Context, key).Result()
	if err != nil && err != redis.Nil {
		logger.ErrorString("Redis", "LRange", err.Error())
		return 0
	}
	return res
}
